package com.nekonomics
package persistence.repository

import domain.entity.CartItem
import utils.SqlUtil
import utils.magic.SqlMagic

import cats.data.NonEmptyList
import doobie.*
import doobie.implicits.*
import doobie.postgres.implicits.*
import zio.interop.catz.*
import zio.{ULayer, ZLayer}

trait CartItemRepo {
  def upsert(userId: Long, skuId: Long, quantity: Int): ConnectionIO[Long]

  def listPageByUserId(userId: Long, pageNum: Int, pageSize: Int): ConnectionIO[List[CartItem]]

  def countByUserId(userId: Long): ConnectionIO[Long]

  def listBy(userId: Long, skuIds: List[Long]): ConnectionIO[List[CartItem]]

  def delete(userId: Long, skuIds: List[Long]): ConnectionIO[Int]
}


object CartItemRepo {
  val live: ULayer[CartItemRepo] = ZLayer.succeed(new CartItemRepoLive)
}

class CartItemRepoLive extends CartItemRepo {

  override def upsert(userId: Long, skuId: Long, quantity: Int): ConnectionIO[Long] = {
    sql"""
      INSERT INTO public.cart_item
        (user_id, sku_id, quantity)
      VALUES
        ($userId, $skuId, $quantity)
      ON CONFLICT (user_id, sku_id)
      DO UPDATE SET
        quantity = cart_item.quantity + EXCLUDED.quantity,
        updated_at = now()
      RETURNING id
    """
      .query[Long]
      .unique
  }


  override def countByUserId(userId: Long): ConnectionIO[Long] =
    sql"select count(id) from public.cart_item where user_id = $userId"
      .query[Long]
      .unique


  override def listPageByUserId(userId: Long, pageNum: Int, pageSize: Int): ConnectionIO[List[CartItem]] = {
    val fields = SqlMagic.fieldSnakeNames4Select[CartItem]
    (
      fr"select "
        ++ Fragment.const(fields)
        ++ fr"from public.cart_item where user_id=$userId"
        ++ fr"limit $pageSize offset ${pageSize * (pageNum - 1)}"
      )
      .query[CartItem]
      .to[List]
  }


  override def listBy(userId: Long, skuIds: List[Long]): doobie.ConnectionIO[List[CartItem]] = {
    val cond = Fragments.and(fr"user_id=$userId", Fragments.in(fr"sku_id", NonEmptyList.fromListUnsafe(skuIds)))
    SqlUtil.selectFromWhereOpt[CartItem](Some(cond))
      .query[CartItem]
      .to[List]
  }


  override def delete(userId: Long, skuIds: List[Long]): doobie.ConnectionIO[Int] = {
    val inFr = Fragments.in(fr"sku_id", NonEmptyList.fromListUnsafe(skuIds))
    val whereCond = Fragments.whereAnd(fr"user_id = $userId", inFr)
    (fr"delete from public.cart_item" ++ whereCond)
      .update
      .run
  }
}
